#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <algorithm> 
#include <stddef.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>


#include <stdint.h>

#include "PlayWav.h"

// 解析WAV文件头
int ParseWav(const char* filename, unsigned int* sample_rate, unsigned short* channels, unsigned short* bits_per_sample) 
{
    if (!sample_rate || !channels || !bits_per_sample) 
    {
        fprintf(stderr, "参数错误：指针不能为空\n");
        return -1;
    }

    FILE* fp = fopen(filename, "rb");
    if (!fp) 
    {
        perror("文件打开失败");
        return -1;
    }

    // 解析RIFF块
    RIFF_CHUNK riff;
    size_t read_size = fread(&riff, 1, sizeof(RIFF_CHUNK), fp);
    if (read_size != sizeof(RIFF_CHUNK)) 
    {
        fprintf(stderr, "RIFF块读取失败（实际%zu字节，预期%zu字节）\n", read_size, sizeof(RIFF_CHUNK));
        fclose(fp);
        return -1;
    }

    printf("检测到的标识: ChunkID=%.4s, Format=%.4s\n", riff.ChunkID, riff.Format);
    if (memcmp(riff.ChunkID, "RIFF", 4) != 0 || memcmp(riff.Format, "WAVE", 4) != 0) 
    {
        fprintf(stderr, "不是有效的WAV文件\n");
        fclose(fp);
        return -1;
    }

    // 查找并解析fmt块
    PCMWAVEFORMAT pcmFmt = {0};
    FACT_CHUNK fact = {0};
    int foundFmt = 0, foundFact = 0;
    CHAR subchunkID[4];
    DWORD subchunkSize;

    while (1) 
    {
        if (fread(subchunkID, 1, 4, fp) != 4 || fread(&subchunkSize, sizeof(DWORD), 1, fp) != 1) 
        {
            perror("子块信息读取失败");
            fclose(fp);
            return -1;
        }

        if (memcmp(subchunkID, "fmt ", 4) == 0) 
        {
            if (subchunkSize < sizeof(PCMWAVEFORMAT)) 
            {
                fprintf(stderr, "fmt块大小异常（%u字节 < %zu字节）\n", subchunkSize, sizeof(PCMWAVEFORMAT));
                fclose(fp);
                return -1;
            }
            if (fread(&pcmFmt, sizeof(PCMWAVEFORMAT), 1, fp) != 1) 
            {
                perror("fmt块内容读取失败");
                fclose(fp);
                return -1;
            }
            if (subchunkSize > sizeof(PCMWAVEFORMAT)) 
            {
                fseek(fp, subchunkSize - sizeof(PCMWAVEFORMAT), SEEK_CUR);
            }
            
            // 提取参数
            *sample_rate = pcmFmt.wf.nSamplesPerSec;
            *channels = pcmFmt.wf.nChannels;
            *bits_per_sample = pcmFmt.wBitsPerSample;
            
            foundFmt = 1;
            break;
        }
        else if (memcmp(subchunkID, "fact", 4) == 0) 
        {
            foundFact = 1;
            memcpy(fact.SubchunkID, subchunkID, 4);
            fact.SubchunkSize = subchunkSize;
            if (subchunkSize >= sizeof(DWORD)) 
            {
                fread(&fact.dwSampleLength, sizeof(DWORD), 1, fp);
            }
            if (subchunkSize > sizeof(DWORD)) 
            {
                fseek(fp, subchunkSize - sizeof(DWORD), SEEK_CUR);
            }
        }
        else 
        {
            fseek(fp, subchunkSize, SEEK_CUR);
        }

        if (ftell(fp) > (long)(riff.ChunkSize + 8)) 
        {
            fprintf(stderr, "未找到fmt块\n");
            fclose(fp);
            return -1;
        }
    }

    // 解析data块
    DATA_CHUNK dataChunk = {0};
    int foundData = 0;
    while (1) 
    {
        if (fread(dataChunk.SubchunkID, 1, 4, fp) != 4 || fread(&dataChunk.SubchunkSize, sizeof(DWORD), 1, fp) != 1) 
        {
            perror("data块信息读取失败");
            fclose(fp);
            return -1;
        }

        if (memcmp(dataChunk.SubchunkID, "data", 4) == 0) 
        {
            foundData = 1;
            break;
        } 
        else if (memcmp(dataChunk.SubchunkID, "fact", 4) == 0 && !foundFact) 
        {
            foundFact = 1;
            fact.SubchunkSize = dataChunk.SubchunkSize;
            if (dataChunk.SubchunkSize >= sizeof(DWORD)) 
            {
                fread(&fact.dwSampleLength, sizeof(DWORD), 1, fp);
            }
            if (dataChunk.SubchunkSize > sizeof(DWORD)) 
            {
                fseek(fp, dataChunk.SubchunkSize - sizeof(DWORD), SEEK_CUR);
            }
        }
        else 
        {
            fseek(fp, dataChunk.SubchunkSize, SEEK_CUR);
        }

        if (ftell(fp) > (long)(riff.ChunkSize + 8)) 
        {
            fprintf(stderr, "未找到data块\n");
            fclose(fp);
            return -1;
        }
    }

    // 打印头信息
    printf("===== WAV 文件头信息 =====\n");
    printf("RIFF 块:\n");
    printf("  标识: %.4s\n", riff.ChunkID);
    printf("  总大小: %u 字节(不含 RIFF 头)\n", riff.ChunkSize);
    printf("  格式: %.4s\n", riff.Format);

    printf("\nWAVEFORMAT:\n");
    printf("  编码格式: %u (%s)\n", pcmFmt.wf.wFormatTag,
           pcmFmt.wf.wFormatTag == 1 ? "WAVE_FORMAT_PCM" : "非 PCM");
    printf("  声道数: %u\n", pcmFmt.wf.nChannels);
    printf("  采样率: %u Hz\n", pcmFmt.wf.nSamplesPerSec);
    printf("  平均字节率: %u 字节/秒\n", pcmFmt.wf.nAvgBytesPerSec);
    printf("  块对齐: %u 字节\n", pcmFmt.wf.nBlockAlign);

    printf("\nPCMWAVEFORMAT:\n");
    printf("  采样位数: %u bits\n", pcmFmt.wBitsPerSample);

    if (foundFact) 
    {
        printf("\nfact 块:\n");
        printf("  标识: %.4s\n", fact.SubchunkID);
        printf("  块大小: %u 字节\n", fact.SubchunkSize);
        printf("  总采样帧数: %u\n", fact.dwSampleLength);
        printf("  音频时长: %.2f 秒（基于采样帧）\n",
               (float)fact.dwSampleLength / pcmFmt.wf.nSamplesPerSec);
    }

    printf("\ndata 块:\n");
    printf("  标识: %.4s\n", dataChunk.SubchunkID);
    printf("  数据大小: %u 字节\n", dataChunk.SubchunkSize);
    if (!foundFact) 
    {
        printf("  音频时长: %.2f 秒(估算)\n",
               (float)dataChunk.SubchunkSize / pcmFmt.wf.nAvgBytesPerSec);
    }

    fclose(fp);
    return 0;
}

// 写入WAV文件头
void write_wav_header(int fd, unsigned int sample_rate, unsigned short channels, unsigned short bits_per_sample, int duration) 
{
    WAV_HEADER header = {0};
    DWORD byte_rate = sample_rate * channels * (bits_per_sample / 8);
    WORD block_align = channels * (bits_per_sample / 8);
    DWORD data_size = duration * byte_rate;  // 预估数据大小

    // 填充RIFF块
    memcpy(header.riff.ChunkID, "RIFF", 4);
    header.riff.ChunkSize = data_size + sizeof(WAV_HEADER) - 8;  // 总大小计算
    memcpy(header.riff.Format, "WAVE", 4);

    // 填充fmt块
    memcpy(header.fmt_id, "fmt ", 4);
    header.fmt_size = sizeof(PCMWAVEFORMAT);
    header.fmt.wf.wFormatTag = 1;  // PCM格式
    header.fmt.wf.nChannels = channels;
    header.fmt.wf.nSamplesPerSec = sample_rate;
    header.fmt.wf.nAvgBytesPerSec = byte_rate;
    header.fmt.wf.nBlockAlign = block_align;
    header.fmt.wBitsPerSample = bits_per_sample;

    // 填充data块
    memcpy(header.data.SubchunkID, "data", 4);
    header.data.SubchunkSize = data_size;

    // 写入完整头部
    write(fd, &header, sizeof(WAV_HEADER));
}

// 检查是否为WAV文件
bool IsAudioFile(const std::string &strFileName)
{
    size_t pos = strFileName.rfind('.');
    if (pos == std::string::npos) return false;

    std::string extension = strFileName.substr(pos);
    std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
    return extension == ".wav";
}

// 帮助信息
void ShowHelpInfo()
{
    printf("Usage: \n");
    printf("Play   : ./AlsaTool <wav file>\n");
    printf("Record : ./AlsaTool\n");
}

// 设置PCM硬件参数
static int set_hw_params(snd_pcm_t *pcmHandler, unsigned int sample_rate, unsigned short channels, unsigned short bits_per_sample) 
{
    snd_pcm_hw_params_t *hwParams;
    int ret;
    int dir;
    snd_pcm_format_t pcm_format;

    // 映射采样位数到ALSA格式
    if (bits_per_sample == 8)
        pcm_format = SND_PCM_FORMAT_U8;
    else if (bits_per_sample == 16)
        pcm_format = SND_PCM_FORMAT_S16_LE;
    else if (bits_per_sample == 24)
        pcm_format = SND_PCM_FORMAT_S24_LE;
    else if (bits_per_sample == 32)
        pcm_format = SND_PCM_FORMAT_S32_LE;
    else
    {
        fprintf(stderr, "不支持的采样位数: %u bits\n", bits_per_sample);
        return -1;
    }

    snd_pcm_hw_params_malloc(&hwParams);
    do {
        ret = snd_pcm_hw_params_any(pcmHandler, hwParams);
        if (ret < 0) {
            fprintf(stderr, "snd_pcm_hw_params_any error: %s\n", snd_strerror(ret));
            break;
        }

        ret = snd_pcm_hw_params_set_access(pcmHandler, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
        if (ret < 0) {
            fprintf(stderr, "set_access error: %s\n", snd_strerror(ret));
            break;
        }

        ret = snd_pcm_hw_params_set_format(pcmHandler, hwParams, pcm_format);
        if (ret < 0) {
            fprintf(stderr, "set_format error: %s\n", snd_strerror(ret));
            break;
        }

        ret = snd_pcm_hw_params_set_channels(pcmHandler, hwParams, channels);
        if (ret < 0) {
            fprintf(stderr, "set_channels error: %s\n", snd_strerror(ret));
            break;
        }

        ret = snd_pcm_hw_params_set_rate_near(pcmHandler, hwParams, &sample_rate, &dir);
        if (ret < 0) {
            fprintf(stderr, "set_rate error: %s\n", snd_strerror(ret));
            break;
        }
        printf("实际设置的采样率: %u Hz\n", sample_rate);

        unsigned int period_time = 10000;  // 10ms周期
        ret = snd_pcm_hw_params_set_period_time_near(pcmHandler, hwParams, &period_time, &dir);
        if (ret < 0) {
            fprintf(stderr, "set_period_time error: %s\n", snd_strerror(ret));
            break;
        }

        ret = snd_pcm_hw_params(pcmHandler, hwParams);
        if (ret < 0) {
            fprintf(stderr, "apply hw_params error: %s\n", snd_strerror(ret));
            break;
        }

        snd_pcm_hw_params_free(hwParams);
        return 0;
    } while (0);

    snd_pcm_hw_params_free(hwParams);
    return -1;
}

// 录音函数
int RecordAudio(const char *filename, int duration) 
{
    printf("Calling RecordAudio() filename = %s, duration = %d\n", filename, duration);
    unsigned int sample_rate = 48000;
    unsigned short channels = 2;
    unsigned short bits_per_sample = 16;
    
    snd_pcm_t *pcm_handle = NULL;
    snd_pcm_uframes_t period_size;
    unsigned char *buff = NULL;
    unsigned int buf_bytes;
    int ret = -1;
    int fd = -1;
    snd_pcm_hw_params_t *temp_hw_params = NULL;

    do {
        // 打开PCM捕获设备
        ret = snd_pcm_open(&pcm_handle, "hw:0,0", SND_PCM_STREAM_CAPTURE, 0);
        if (ret < 0) {
            printf("Open PCM device error: %s\n", snd_strerror(ret));
            break;
        }

        // 设置硬件参数
        if (set_hw_params(pcm_handle, sample_rate, channels, bits_per_sample) < 0) {
            snd_pcm_close(pcm_handle);
            break;
        }

        // 获取周期大小
        snd_pcm_hw_params_malloc(&temp_hw_params);
        ret = snd_pcm_hw_params_current(pcm_handle, temp_hw_params);
        if (ret < 0) {
            fprintf(stderr, "get current hw_params error: %s\n", snd_strerror(ret));
            break;
        }
        snd_pcm_hw_params_get_period_size(temp_hw_params, &period_size, NULL);
        snd_pcm_hw_params_free(temp_hw_params);

        // 分配缓冲区
        buf_bytes = period_size * channels * (bits_per_sample / 8);
        buff = (unsigned char *)malloc(buf_bytes);
        if (buff == NULL) {
            printf("Malloc buffer failed\n");
            break;
        }

        // 打开文件
        fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (fd < 0) {
            perror("Open file failed");
            break;
        }

        // 写入WAV头
        write_wav_header(fd, sample_rate, channels, bits_per_sample, duration);

        // 录音循环
        unsigned int total_periods = (duration * sample_rate) / period_size;
        for (unsigned int i = 0; i < total_periods; i++) {
            ret = snd_pcm_readi(pcm_handle, buff, period_size);
            if (ret < 0) {
                fprintf(stderr, "snd_pcm_readi error: %s\n", snd_strerror(ret));
                ret = snd_pcm_recover(pcm_handle, ret, 0);
                if (ret < 0) {
                    fprintf(stderr, "Recover PCM failed: %s\n", snd_strerror(ret));
                    break;
                }
                continue;
            }

            int write_len = ret * channels * (bits_per_sample / 8);
            if (write(fd, buff, write_len) != write_len) {
                perror("Write file failed");
                break;
            }
        }

        // 修正文件头
        if (fd != -1 && ret >= 0) {
            off_t total_file_size = lseek(fd, 0, SEEK_END);
            DWORD real_data_size = total_file_size - sizeof(WAV_HEADER);
            DWORD real_riff_size = real_data_size + sizeof(WAV_HEADER) - 8;

            lseek(fd, offsetof(WAV_HEADER, data.SubchunkSize), SEEK_SET);
            write(fd, &real_data_size, sizeof(real_data_size));

            lseek(fd, offsetof(WAV_HEADER, riff.ChunkSize), SEEK_SET);
            write(fd, &real_riff_size, sizeof(real_riff_size));

            printf("录音完成，实际数据大小: %u 字节\n", real_data_size);
        }

        if (ret >= 0) printf("Recording completed successfully.\n");
        ret = 0;
    } while (0);

    // 资源清理
    if (fd != -1) close(fd);
    if (buff != NULL) free(buff);
    if (pcm_handle != NULL) snd_pcm_close(pcm_handle);

    return ret;
}

// 播放函数
int PlayWavFile(const char * cstrFileName)
{
    printf("Start Play Wav File %s\n", cstrFileName);

    // 解析WAV参数
    unsigned int sample_rate;
    unsigned short channels, bits_per_sample;
    if (ParseWav(cstrFileName, &sample_rate, &channels, &bits_per_sample) != 0)
    {
        fprintf(stderr, "WAV文件解析失败，无法播放\n");
        return -1;
    }

    // 打开WAV文件
    FILE *file = fopen(cstrFileName, "rb");
    if (!file) 
    {
        perror("fopen");
        return 1;
    }

    // 定位到音频数据（跳过WAV头）
    fseek(file, sizeof(WAV_HEADER), SEEK_SET);

    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t period_size;
    int err;

    // 打开播放设备
    err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (err < 0) 
    {
        fprintf(stderr, "cannot open audio device %s (%s)\n", "default", snd_strerror(err));
        fclose(file);
        return 1;
    }

    // 设置播放参数
    snd_pcm_hw_params_alloca(&params);
    err = snd_pcm_hw_params_any(handle, params);
    if (err < 0)
    {
        fprintf(stderr, "snd_pcm_hw_params_any error: %s\n", snd_strerror(err));
        snd_pcm_close(handle);
        fclose(file);
        return 1;
    }

    if (set_hw_params(handle, sample_rate, channels, bits_per_sample) < 0)
    {
        snd_pcm_close(handle);
        fclose(file);
        return 1;
    }

    // 获取周期大小
    snd_pcm_hw_params_get_period_size(params, &period_size, NULL);
    unsigned int buf_bytes = period_size * channels * (bits_per_sample / 8);
    unsigned char *buffer = (unsigned char *)malloc(buf_bytes);
    if (!buffer) 
    {
        fprintf(stderr, "malloc failed\n");
        snd_pcm_close(handle);
        fclose(file);
        return 1;
    }

    // 播放循环
    while (1) 
    {
        size_t read_bytes = fread(buffer, 1, buf_bytes, file);
        if (read_bytes == 0) 
        {
            if (feof(file)) 
            {
                printf("播放完成（已到文件末尾）\n");
                break;
            } 
            else 
            {
                fprintf(stderr, "fread failed\n");
                break;
            }
        }

        snd_pcm_uframes_t play_frames = read_bytes / (channels * (bits_per_sample / 8));
        err = snd_pcm_writei(handle, buffer, play_frames);
        if (err < 0) 
        {
            fprintf(stderr, "write error: %s\n", snd_strerror(err));
            err = snd_pcm_recover(handle, err, 0);
            if (err < 0) 
            {
                fprintf(stderr, "Recover PCM failed: %s\n", snd_strerror(err));
                break;
            }
            continue;
        }
    }

    // 清理资源
    free(buffer);
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    fclose(file);
    return 0;
}

// 主函数
int TestAudioMain(int argc, char *argv[]) 
{
    if (argc == 1) 
    {
        RecordAudio("Record.wav", 10);  // 录制10秒
        return 0;
    } 
    else if (argc == 2) 
    {
        char cstrArg[256] = {0};
        strncpy(cstrArg, argv[1], sizeof(cstrArg)-1);
        if (IsAudioFile(cstrArg)) 
        {
            PlayWavFile(cstrArg);
        }
        else
        {
            fprintf(stderr, "仅支持WAV格式文件\n");
            ShowHelpInfo();
        }
    } 
    else 
    {
        ShowHelpInfo();
    }
    return 0;
}